//
// Created by work on 24-9-14.
//
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>

#include "cJSON.h"
#include "MQTTClient.h"

#define ADDRESS     "127.0.0.1:1883"

#define USERNAME    "apple"
#define PASSWORD    "apple220724"
#define CLIENTID    "c-client"
#define QOS         2

#define PUB_LOGIN_TOPIC       "user/login"
#define SUB_LOGINRES_TOPIC    "user/loginresponse"

#define SUB_MSG_TOPIC         "device/ctl"
#define PUB_MSG_TOPICRES      "device/ctl_response"

#define TIMEOUT     10000L

typedef struct SRtDeviceInfo {
    char* user;
    char* model_name;
    char* name;
    char* descr;
    int sn;
    char* grouping;
    char* create_time;
    char* update_time;
    char* location;
    int protocol;
    bool valid;
} SRtDeviceInfo;

MQTTClient client;

enum LoginState {
    DisLogin,
    LoginIng,
    LoginOk
};

enum LoginState m_loginstate = DisLogin;
pthread_mutex_t lock_recv;
pthread_cond_t cond_recv;

char recvbuf[1000];
int recvbufsize = 0;
bool recvbusy = false;

SRtDeviceInfo info = {
    .user = "testuser",
    .model_name = "TestModel",
    .name = "DeviceName",
    .descr = "This is a long description of the device.",
    .sn = 12345,
    .grouping = "GroupA",
    .create_time = "2023-01-01T00:00:00Z",
    .update_time = "2023-01-02T00:00:00Z",
    .location = "LocationX",
    .protocol = 1,
    .valid = 1
};

static cJSON *SRtDeviceInfo_to_cJSON(SRtDeviceInfo *info) {
    cJSON *root = cJSON_CreateObject();
    if (root == NULL) {
        fprintf(stderr, "Failed to create JSON object\n");
        return NULL;
    }

    cJSON_AddStringToObject(root, "userid", info->user);
    cJSON_AddStringToObject(root, "model_name", info->model_name);
    cJSON_AddStringToObject(root, "name", info->name);
    cJSON_AddStringToObject(root, "descr", info->descr);
    cJSON_AddNumberToObject(root, "sn", info->sn);
    cJSON_AddStringToObject(root, "grouping", info->grouping);
    cJSON_AddStringToObject(root, "create_time", info->create_time);
    cJSON_AddStringToObject(root, "update_time", info->update_time);
    cJSON_AddStringToObject(root, "location", info->location);
    cJSON_AddNumberToObject(root, "protocol", info->protocol);
    cJSON_AddBoolToObject(root, "valid", info->valid);

    return root;
}

static void publish(MQTTClient client, char *topic, char *payload) {
    MQTTClient_message message = MQTTClient_message_initializer;
    message.payload = payload;
    message.payloadlen = strlen(payload);
    message.qos = QOS;
    message.retained = 0;
    MQTTClient_deliveryToken token;
    MQTTClient_publishMessage(client, topic, &message, &token);
    MQTTClient_waitForCompletion(client, token, TIMEOUT);
    printf("Send `%s` to topic `%s` \n", payload, topic);
}

int on_message(void *context, char *topicName, int topicLen, MQTTClient_message *message) {
    char *payload = message->payload;
    printf("Received `%s` from `%s` topic \n", payload, topicName);

    if(!recvbusy) {
        strncpy(recvbuf, payload, message->payloadlen);
        recvbufsize = message->payloadlen;
        pthread_cond_signal(&cond_recv);
    }

    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

void* threadRecvHandler(void*) {
    while (true) {
        pthread_mutex_lock(&lock_recv);
        pthread_cond_wait(&cond_recv, &lock_recv);
        recvbusy = true;
        char msg[recvbufsize];
        strncpy(msg, recvbuf, recvbufsize);
        cJSON *json = cJSON_Parse(msg);
        if (json == NULL) {
            const char *error_ptr = cJSON_GetErrorPtr();
            if (error_ptr != NULL) {
                fprintf(stderr, "Error before: %s\n", error_ptr);
            }
            recvbusy = false;
            pthread_mutex_unlock(&lock_recv);
            continue;
        }

        cJSON *topic = cJSON_GetObjectItemCaseSensitive(json, "topic");
        if (!cJSON_IsString(topic) || (topic->valuestring == NULL)) {
            recvbusy = false;
            pthread_mutex_unlock(&lock_recv);
            continue;
        }

        cJSON *userid = cJSON_GetObjectItemCaseSensitive(json, "userid");
        if (!cJSON_IsString(userid) || (userid->valuestring == NULL)) {
            recvbusy = false;
            pthread_mutex_unlock(&lock_recv);
            continue;
        }

        cJSON* model_name = cJSON_GetObjectItemCaseSensitive(json, "model_name");
        if (!cJSON_IsString(model_name) || (model_name->valuestring == NULL)) {
            recvbusy = false;
            pthread_mutex_unlock(&lock_recv);
            continue;
        }

        cJSON* name = cJSON_GetObjectItemCaseSensitive(json, "name");
        if (!cJSON_IsString(name) || (name->valuestring == NULL)) {
            recvbusy = false;
            pthread_mutex_unlock(&lock_recv);
            continue;
        }

        if(strcmp(topic->valuestring, SUB_LOGINRES_TOPIC) == 0 && m_loginstate == LoginIng) {
            if(strcmp(userid->valuestring, info.user) == 0 &&
               strcmp(model_name->valuestring, info.model_name) == 0 &&
               strcmp(name->valuestring, info.name) == 0 ) {
                m_loginstate = LoginOk;
                printf("Login success\n");
            }
        } else {
            if (strcmp(userid->valuestring, info.user) != 0 &&
                strcmp(model_name->valuestring, info.model_name) != 0 &&
                strcmp(name->valuestring, info.name) != 0) {
                recvbusy = false;
                pthread_mutex_unlock(&lock_recv);
                continue;
            }

            cJSON *root = cJSON_CreateObject();
            cJSON_AddBoolToObject(root, "state", 1); // 注意：cJSON使用1表示true，0表示false
            char *rendered = cJSON_Print(root);
            publish(client, PUB_MSG_TOPICRES, rendered);
            cJSON_Delete(root);
            free(rendered);
        }
        recvbusy = false;
        pthread_mutex_unlock(&lock_recv);
    }
}

int main(int argc, char *argv[]) {

    /*初始化MQtt客户端以及连接参数*/
    int rc;
    MQTTClient_create(&client, ADDRESS, CLIENTID, 0, NULL);
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    conn_opts.username = USERNAME;
    conn_opts.password = PASSWORD;

    /*连接mqtt服务器*/
    MQTTClient_setCallbacks(client, (void*)&client, NULL, on_message, NULL);
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) {
        printf("Failed to connect, return code %d\n", rc);
        exit(-1);
    } else {
        printf("Connected to MQTT Broker!\n");
    }

    /*订阅登陆响应主题和控制请求主题消息*/
    MQTTClient_subscribe(client, SUB_LOGINRES_TOPIC, 2);

    /*创建解析报文线程*/
    pthread_t threadId;
    int ret = pthread_create(&threadId, NULL, threadRecvHandler, NULL);
    if (ret != 0) {
        printf("Failed to create thread\n");
        exit(EXIT_FAILURE);
    }

    /*发送设备信息到服务器认证登陆*/
    cJSON *jsondevice = SRtDeviceInfo_to_cJSON(&info);
    if (jsondevice != NULL) {
        char *json_string = cJSON_Print(jsondevice);
        publish(client, PUB_LOGIN_TOPIC, json_string);
        m_loginstate = LoginIng;
        free(json_string);
        cJSON_Delete(jsondevice);
    }

    while (m_loginstate != LoginOk) {
        sleep(1);
    }

    MQTTClient_subscribe(client, SUB_MSG_TOPIC, 2);

    ret = pthread_join(threadId, NULL);
    if (ret != 0) {
        perror("无法等待线程");
        exit(EXIT_FAILURE);
    }
    return 0;
}

